-------------------------------------------------------------------------------
-- TITLE      : PROGRAM COUNTER
-- PROJECT    : ANEM 16 BITS
-------------------------------------------------------------------------------
-- FILE       : PC.VHD
-- AUTHOR     : IAN ANTÔNIO
-- COMPANY    : 
-- CREATED    : 2011-04-16
-- LAST UPDATE: 2011-04-29
-- PLATFORM   : 
-- STANDARD   : VHDL'87
-------------------------------------------------------------------------------
-- DESCRIPTION: DESCRIÇÃO DO CONTADOR DE PROGRAMA DO ANEM 16 BITS MONO-CICLO.
-------------------------------------------------------------------------------

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY PC IS
PORT(	TEST		:IN STD_LOGIC;				--SINAL DE CONTROLE DO TESTE/DEBUG.
	S_IN_PC		:IN STD_LOGIC;				--SINAL DE ENTRADA DO TESTE/DEBUG.
	S_OUT_PC	:OUT STD_LOGIC;				--SINAL DE SAÍDA DO TESTE/DEBUG.
	PC_CONT		:IN STD_LOGIC_VECTOR(1 DOWNTO 0);	--OPERAÇÃO
  	ENDERECO	:IN STD_LOGIC_VECTOR(11 DOWNTO 0);	--JUMP J
  	A_OUT		:IN STD_LOGIC_VECTOR(15 DOWNTO 0);	--JUMP JR
  	OFFSET		:IN STD_LOGIC_VECTOR(3 DOWNTO 0);	--JUMP BEQ - OFFSET A SER SOMADO NO CONTADOR.
 	Z		:IN STD_LOGIC;				--JUMP BEQ - INDICA SE HAVERÁ JUMP.
  	RST, CLK	:IN STD_LOGIC;				--RESET E CLOCK
  	PC_OUT		:OUT STD_LOGIC_VECTOR(15 DOWNTO 0));	--SAÍDA PARA A MEMÓRIA DE PROGRAMA.
END ENTITY PC;

ARCHITECTURE BEHAVIOR OF PC IS
BEGIN
  PROCESS(CLK,RST)					
  VARIABLE COUNTER	:STD_LOGIC_VECTOR(15 DOWNTO 0)	:=	(OTHERS=>'0');		--GUARDA O VALOR DO PC
  VARIABLE CARRY	:STD_LOGIC_VECTOR(16 DOWNTO 0) := (OTHERS=>'0');		--USADO NAS DIVERSAS SOMAS
  VARIABLE OFFLONG	:STD_LOGIC_VECTOR(15 DOWNTO 0);					--AUMENTA O TAMANHO DE 'OFFSET' PARA 16 BITS
  BEGIN		
    IF RST='1' THEN					--SENSÍVEL AO RESET 
      COUNTER := (OTHERS=>'0');	
      S_OUT_PC <= '0';
    ELSIF RISING_EDGE(CLK) THEN				--SENSÍVEL À SUBIDA DO CLOCK
      IF TEST='1' THEN					--CASO ESTEJA EM MODO 'DEBUG'
	S_OUT_PC <= COUNTER(0);	
	COUNTER(14 DOWNTO 0) := COUNTER(15 DOWNTO 1);
	COUNTER(15) := S_IN_PC;
      ELSE						--CASO NÃO ESTEJA EM MODO 'DEBUG'
       S_OUT_PC <= '0';
       CASE PC_CONT IS 						--OPERAÇÕES:
        WHEN "00" =>	CARRY(0) := '1';					--SEM JUMP (INCREMENTA-SE O PC)	
          FOR I IN 0 TO 15 LOOP
            CARRY(I+1) := COUNTER(I) AND CARRY(I);
            COUNTER(I) := COUNTER(I) XOR CARRY(I);
          END LOOP;
          CARRY := (OTHERS=>'0');
        WHEN "01" =>	COUNTER (11 DOWNTO 0) := ENDERECO;			--JUMP J (ENDEREÇO DADO PELO VETOR DE INSTRUÇÃO)
        WHEN "10" =>	COUNTER := A_OUT;					--JUMP JR (ENDEREÇO DADO PELO REGISTRADOR A)
        WHEN "11" =>	IF Z='1' THEN						--JUMP BEQ
            OFFLONG(3 DOWNTO 0) := OFFSET;						--SE Z=1, FAZER PC+OFFSET.			
            IF OFFSET(3) = '1' THEN					  			--SE FOR NÚMERO NEGATIVO, CONCATENAR '1' E SOMAR.
              OFFLONG(15 DOWNTO 4) := (OTHERS=>'1');
            ELSE							  			--SE FOR NÚMERO POSITIVO, CONCATENAR '0' E SOMAR.
              OFFLONG(15 DOWNTO 4) := (OTHERS=>'0');
            END IF;
            FOR I IN 0 TO 15 LOOP
              CARRY(I+1) := (COUNTER(I) AND OFFLONG(I)) OR (COUNTER(I) AND CARRY(I)) OR (OFFLONG(I) AND CARRY(I));
              COUNTER(I) := COUNTER(I) XOR OFFLONG(I) XOR CARRY(I);
            END LOOP;
          ELSE										--SE Z=0, NÃO HÁ JUMP (INCREMENTA-SE O PC).
            CARRY(0) := '1';
            FOR I IN 0 TO 15 LOOP
              CARRY(I+1) := COUNTER(I) AND CARRY(I);
              COUNTER(I) := COUNTER(I) XOR CARRY(I);
            END LOOP;						
          END IF;
          CARRY := (OTHERS=>'0');
        WHEN OTHERS => NULL;
       END CASE;
      END IF;
  END IF;
  PC_OUT <= COUNTER;
  END PROCESS;
END ARCHITECTURE BEHAVIOR;					--FIM
